#include "CircleRenderer.h"
#include <iostream>
#include <cmath>

using namespace SolidityEngine;

CircleRenderer::CircleRenderer(RenderTarget target):
    Renderer(target),
    _radius(1.0f),
    _filled(true)
{
    //ctor
}

CircleRenderer::~CircleRenderer()
{
    //dtor
}

void CircleRenderer::_Render(SDL_Surface *surface){
    SDL_LockSurface(surface);
    Vector2D position=GetGameObject()->GetComponent<Transform2D>()->Position();
    if(_filled){
        _FillCircle(surface,(int)position.X(),(int)position.Y(),_radius,Color().GetPixelValue(surface));
    }
    else{
        _DrawCircle(surface,(int)position.X(),(int)position.Y(),_radius,Color().GetPixelValue(surface));
    }
    SDL_UnlockSurface(surface);
}

void CircleRenderer::_FillCircle(SDL_Surface *surface, int cx, int cy, int radius, Uint32 pixel)
{
    if((cx+radius>surface->w)||(cx-radius<0)||(cy-radius<0)||(cy+radius>surface->h)){
       return;
    }
    static const int BPP = 4;
    double r = (double)radius;
    for (double dy = 1; dy <= r; dy += 1.0)
    {
        double dx = floor(sqrt((2.0 * r * dy) - (dy * dy)));
        int x = cx - dx;

        // Grab a pointer to the left-most pixel for each half of the circle
        Uint8 *target_pixel_a = (Uint8 *)surface->pixels + ((int)(cy + r - dy)) * surface->pitch + x * BPP;
        Uint8 *target_pixel_b = (Uint8 *)surface->pixels + ((int)(cy - r + dy)) * surface->pitch + x * BPP;

        for (; x <= cx + dx; x++)
        {
            *(Uint32 *)target_pixel_a = pixel;
            *(Uint32 *)target_pixel_b = pixel;
            target_pixel_a += BPP;
            target_pixel_b += BPP;
        }
    }
}

void CircleRenderer::_DrawCircle(SDL_Surface *surface, int n_cx, int n_cy, int radius, Uint32 pixel)
{
    double error = (double)-radius;
    double x = (double)radius -0.5;
    double y = (double)0.5;
    double cx = n_cx - 0.5;
    double cy = n_cy - 0.5;

    while (x >= y)
    {
        SetPixel32(surface, (int)(cx + x), (int)(cy + y), pixel);
        SetPixel32(surface, (int)(cx + y), (int)(cy + x), pixel);

        if (x != 0)
        {
            SetPixel32(surface, (int)(cx - x), (int)(cy + y), pixel);
            SetPixel32(surface, (int)(cx + y), (int)(cy - x), pixel);
        }

        if (y != 0)
        {
            SetPixel32(surface, (int)(cx + x), (int)(cy - y), pixel);
            SetPixel32(surface, (int)(cx - y), (int)(cy + x), pixel);
        }

        if (x != 0 && y != 0)
        {
            SetPixel32(surface, (int)(cx - x), (int)(cy - y), pixel);
            SetPixel32(surface, (int)(cx - y), (int)(cy - x), pixel);
        }

        error += y;
        ++y;
        error += y;

        if (error >= 0)
        {
            --x;
            error -= x;
            error -= x;
        }
    }
}

CircleRenderer* CircleRenderer::_Clone(){
    return new CircleRenderer(*this);
}

